#pragma once

//
// This class is responsible for managing client apps
// that connect to this sensor.  This class also manages
// the raising of events.
//
class CClientsManager : public CAutoCriticalSectionLockable<CClientsManager>,
						public CThread
{
private:
	struct CSensorEvent
	{
		wstring sensorID;
		vector<CDeviceProperty> props;

		CSensorEvent(){}

		CSensorEvent(const CSensorEvent& event)
		{
			sensorID = event.sensorID;
			props = event.props;
		}

		CSensorEvent(const wstring& id, const vector<CDeviceProperty>& props)
		{
			sensorID = id;
			this->props = props;
		}
	};

private:
	map<IWDFFile*, CClient*>				m_mapClients;		// list of active clients
	ULONG									m_ulEventClients;	// number of clients who are interested
																// in receving events
	static CClientsManager					s_ClientsManager;	// single client manager instance
	CComPtr<ISensorClassExtension>			m_spClassExtension;	// reference to the class extension object
	CDataQueue<CSensorEvent>				m_EventsQueue;		// events queue

private:
	CClientsManager(void);
	~CClientsManager(void);

public:
	//
	// Return the singleton client manager instance.
	//
	static CClientsManager& GetClientsManager();

	//
	// Initialize the clients manager.
	//
	bool Init(ISensorClassExtension *pClassExtension);

	//
	// add a new client
	//
	HRESULT AddClient(IWDFFile *pFile, LPWSTR pwszSensorID);

	//
	// remove a client
	//
	HRESULT RemoveClient(IWDFFile *pFile, LPWSTR pwszSensorID);

	//
	// get count of active clients;
	// note that this method returns the actual number of clients;
	// if the same client registers for multiple sensors then this
	// class does not treat them as separate clients
	//
	ULONG GetClientsCount() const;

	//
	// subscribe/unsubscribe to/from events
	//
	HRESULT SubscribeToEvents(IWDFFile *pFile, LPWSTR pwszSensorID);
	HRESULT UnsubscribeFromEvents(IWDFFile *pFile, LPWSTR pwszSensorID);

	//
	// is it worthwhile raising events?
	//
	bool CanRaiseEvents() const;

	//
	// Post a state change event notification if necessary.
	//
	bool PostStateChange(wstring sensorID, SensorState state);

	//
	// Enqueue a data event if necessary.
	//
	bool PostDataEvent(wstring sensorID, vector<CDeviceProperty> &vData);

	//
	// Post a data event.
	//
	bool PostDataEvent(wstring sensorID, CComPtr<IPortableDeviceValues> spValues);

	//
	// override "CThread::run" to manage event posting
	//
	virtual unsigned long run();

protected:
	//
	// raise all pending event notifications
	//
	void ProcessEvents();
};
